<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue3响应式系统可视化</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <style>
      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
      }
      body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        line-height: 1.6;
        color: #333;
        background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
        min-height: 100vh;
        padding: 20px;
      }
      .container {
        max-width: 1200px;
        margin: 0 auto;
      }
      header {
        text-align: center;
        margin-bottom: 30px;
        padding: 20px;
        background: rgba(255, 255, 255, 0.9);
        border-radius: 10px;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
      }
      h1 {
        color: #42b883;
        margin-bottom: 10px;
      }
      .subtitle {
        color: #647eff;
        font-size: 1.2rem;
      }
      .content {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 20px;
      }
      @media (max-width: 900px) {
        .content {
          grid-template-columns: 1fr;
        }
      }
      .panel {
        background: white;
        border-radius: 10px;
        padding: 20px;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
      }
      .panel-title {
        color: #42b883;
        margin-bottom: 15px;
        padding-bottom: 10px;
        border-bottom: 2px solid #eaeaea;
      }
      .reactive-box {
        border: 2px dashed #42b883;
        border-radius: 8px;
        padding: 15px;
        margin: 15px 0;
        background: rgba(66, 184, 131, 0.05);
        min-height: 150px;
      }
      .effect-box {
        border: 2px dashed #647eff;
        border-radius: 8px;
        padding: 15px;
        margin: 15px 0;
        background: rgba(100, 126, 255, 0.05);
        min-height: 150px;
      }
      .data-flow {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin: 20px 0;
        position: relative;
      }
      .flow-step {
        width: 80px;
        height: 80px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        font-weight: bold;
        position: relative;
        z-index: 2;
      }
      .flow-arrow {
        position: absolute;
        top: 50%;
        left: 0;
        right: 0;
        height: 4px;
        background: #ccc;
        transform: translateY(-50%);
        z-index: 1;
      }
      .flow-arrow::after {
        content: "";
        position: absolute;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
        width: 0;
        height: 0;
        border-left: 10px solid #ccc;
        border-top: 10px solid transparent;
        border-bottom: 10px solid transparent;
      }
      .proxy {
        background: #ff7e67;
      }
      .track {
        background: #42b883;
      }
      .trigger {
        background: #647eff;
      }
      .effect {
        background: #a166ff;
      }
      .controls {
        display: flex;
        gap: 10px;
        margin: 20px 0;
      }
      button {
        padding: 10px 15px;
        border: none;
        border-radius: 5px;
        background: #42b883;
        color: white;
        cursor: pointer;
        font-weight: bold;
        transition: all 0.3s;
      }
      button:hover {
        background: #359f70;
        transform: translateY(-2px);
      }
      .data-value {
        font-size: 1.5rem;
        font-weight: bold;
        color: #647eff;
        text-align: center;
        margin: 10px 0;
        padding: 10px;
        border-radius: 5px;
        background: rgba(100, 126, 255, 0.1);
      }
      .log {
        height: 200px;
        overflow-y: auto;
        background: #f8f9fa;
        border-radius: 5px;
        padding: 10px;
        margin-top: 20px;
        border: 1px solid #eaeaea;
        font-family: monospace;
      }
      .log-entry {
        margin-bottom: 5px;
        padding: 5px;
        border-radius: 3px;
      }
      .log-track {
        background: rgba(66, 184, 131, 0.2);
      }
      .log-trigger {
        background: rgba(100, 126, 255, 0.2);
      }
      .log-effect {
        background: rgba(161, 102, 255, 0.2);
      }
      .highlight {
        animation: highlight 1s ease;
      }
      @keyframes highlight {
        0% {
          background-color: rgba(255, 255, 0, 0.5);
        }
        100% {
          background-color: transparent;
        }
      }
      .visualization {
        display: flex;
        flex-direction: column;
        gap: 20px;
      }
      .viz-step {
        padding: 15px;
        border-radius: 8px;
        background: white;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      }
      .viz-title {
        font-weight: bold;
        margin-bottom: 10px;
        color: #42b883;
      }
      .code-block {
        background: #2d2d2d;
        color: #f8f8f2;
        padding: 15px;
        border-radius: 5px;
        font-family: monospace;
        overflow-x: auto;
        margin: 10px 0;
      }
    </style>
  </head>
  <body>
    <div id="app" class="container">
      <header>
        <h1>Vue3响应式系统可视化</h1>
        <p class="subtitle">
          展示数据如何被代理、追踪(track)和触发(trigger)更新
        </p>
      </header>

      <div class="content">
        <div class="panel">
          <h2 class="panel-title">响应式数据流</h2>

          <div class="data-flow">
            <div class="flow-step proxy">代理</div>
            <div class="flow-step track">追踪</div>
            <div class="flow-step trigger">触发</div>
            <div class="flow-step effect">Effect</div>
            <div class="flow-arrow"></div>
          </div>

          <div class="reactive-box">
            <h3>响应式对象</h3>
            <div class="data-value">{{ state.count }}</div>
            <p>这是一个被reactive()代理的响应式对象</p>
          </div>

          <div class="effect-box">
            <h3>Effect函数</h3>
            <div class="data-value">{{ effectValue }}</div>
            <p>这个effect依赖于响应式数据state.count</p>
          </div>

          <div class="controls">
            <button @click="increment">增加计数</button>
            <button @click="decrement">减少计数</button>
            <button @click="reset">重置</button>
          </div>

          <div class="log">
            <h3>操作日志</h3>
            <div
              v-for="(entry, index) in logEntries"
              :key="index"
              :class="['log-entry', `log-${entry.type}`]"
            >
              {{ entry.message }}
            </div>
          </div>
        </div>

        <div class="panel">
          <h2 class="panel-title">工作原理</h2>

          <div class="visualization">
            <div class="viz-step">
              <div class="viz-title">1. 创建响应式对象</div>
              <p>使用 reactive() 函数创建代理对象</p>
              <div class="code-block">
                const state = reactive({<br />
                &nbsp;&nbsp;count: 0<br />
                });
              </div>
            </div>

            <div class="viz-step">
              <div class="viz-title">2. 依赖追踪 (Track)</div>
              <p>当在effect中访问响应式属性时，Vue会追踪这个依赖关系</p>
              <div class="code-block">
                effect(() => {<br />
                &nbsp;&nbsp;effectValue = state.count * 2;<br />
                });
              </div>
            </div>

            <div class="viz-step">
              <div class="viz-title">3. 触发更新 (Trigger)</div>
              <p>当响应式属性发生变化时，Vue会触发所有相关的effect</p>
              <div class="code-block">state.count++; // 触发更新</div>
            </div>

            <div class="viz-step">
              <div class="viz-title">4. 执行Effect</div>
              <p>Effect函数重新执行，更新依赖的值</p>
              <div class="code-block">
                effect函数重新执行:<br />
                &nbsp;&nbsp;effectValue = 1 * 2; // 结果为2
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <script>
      const { createApp, reactive, watchEffect, ref } = Vue;

      const app = createApp({
        setup() {
          // 创建响应式状态
          const state = reactive({
            count: 0,
          });

          // Effect的值
          const effectValue = ref(0);

          // 日志条目
          const logEntries = ref([]);

          // 添加日志
          const addLog = (message, type) => {
            logEntries.value.unshift({
              message,
              type,
              timestamp: new Date().toLocaleTimeString(),
            });

            // 保持日志数量不超过10条
            if (logEntries.value.length > 10) {
              logEntries.value.pop();
            }
          };

          // 模拟Effect
          watchEffect(() => {
            effectValue.value = state.count * 2;
            addLog(
              `Effect执行: count = ${state.count}, effectValue = ${effectValue.value}`,
              "effect"
            );
          });

          // 增加计数
          const increment = () => {
            addLog(`开始追踪: 读取count值 (${state.count})`, "track");
            state.count++;
            addLog(`触发更新: count设置为${state.count}`, "trigger");
          };

          // 减少计数
          const decrement = () => {
            addLog(`开始追踪: 读取count值 (${state.count})`, "track");
            state.count--;
            addLog(`触发更新: count设置为${state.count}`, "trigger");
          };

          // 重置
          const reset = () => {
            addLog(`开始追踪: 读取count值 (${state.count})`, "track");
            state.count = 0;
            addLog(`触发更新: count重置为0`, "trigger");
          };

          // 初始日志
          addLog("初始化响应式对象: { count: 0 }", "track");
          addLog("初始化Effect函数", "effect");

          return {
            state,
            effectValue,
            logEntries,
            increment,
            decrement,
            reset,
          };
        },
      });

      app.mount("#app");
    </script>
  </body>
</html>
